#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <fstream>
#include <mutex>

#include "util.hpp"

namespace ns_index
{

    struct DocInfo
    {
        std::string title;   // 文档标题
        std::string content; // 文档内容去标签后的内容
        std::string url;     // 文档url
        long long doc_id;    // 文档id
    };
    
    struct  InvertedElem
    {
        long long doc_id; // 文档id
        std::string word; // 词
        int weight;       // 权重
    };

    typedef std::vector<InvertedElem> InvertedElemList;
    class Index
    {
    private:
        Index() {}
        Index(const Index&) = delete;
        Index& operator=(const Index&) = delete;

        // 单例模式
        static Index* instance; 
        // 锁
        static std::mutex lock;
    public:
        ~Index() {}
    private:
        // 正排索引    文档id 对应文档信息
        std::vector<DocInfo> forward_index;
        // 倒排索引     一个关键字 对应多个文档的 词及权重
        std::unordered_map<std::string, InvertedElemList> inverted_index;

    public:
        // 单例模式
        static Index* GetInstance()
        {
            if(instance == nullptr)
            {
                lock.lock();
                if(instance == nullptr)
                {
                    instance = new Index();
                }
                lock.unlock();
            }
            return instance;
        }
        // 正排索引:    根据doc_id获取
        DocInfo* GetForwardIndex(long long doc_id)
        {
            if(doc_id >= forward_index.size())
            {
                std::cerr << "doc_id out of range" << std::endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }
        // 倒排索引:    根据word获取
        InvertedElemList* GetInvertedIndex(const std::string &word)
        {
            auto iter = inverted_index.find(word);
            if(iter == inverted_index.end())
            {
                std::cerr << "word not found in inverted index" << std::endl;
                return nullptr;
            }
            return &iter->second;
        }
        // 去标签，格式化之后的文档内容在data/raw_html/raw.txt
        // 一、构建索引
        bool BuildIndex(const std::string &file_path)
        {
            std::ifstream in(file_path);
            if(!in.is_open())
            {
                std::cerr << "open file failed" << std::endl;
                return false;
            }

            std::string line;
            while(std::getline(in, line))
            {
                // 构建正排索引
                DocInfo* doc = BuildForwardIndex(line); 
                if(doc == nullptr)
                {
                    std::cerr << "build forward index failed" << std::endl;
                    continue;
                }
                // 构建倒排索引
                BuildInvertedIndex(*doc); 
                if(doc->doc_id % 100 == 0)
                {
                    std::cout << "倒序索引构建进度: " << doc->doc_id << std::endl;
                }
            }
            std::cout << "倒序索引构建完成" << std::endl;
            return true;
        }
    private:
        // 构建正排索引
        DocInfo* BuildForwardIndex(const std::string &line)
        {
            // 1. 解析line，字符串切分
            const std::string sep = "\3";
            std::vector<std::string> results;
            ns_util::StringUtil::CutString(line, &results, sep);
            if(results.size() != 3)
            {
                return nullptr;
            }

            // 2. 字符串进行填充到DocInfo
            DocInfo doc;
            doc.title = results[0];
            doc.content = results[1];
            doc.url = results[2];
            doc.doc_id = forward_index.size(); // forward_index的下标

            // 3. 插入doc_id到forward_index
            forward_index.push_back(std::move(doc));
            return &forward_index.back();
        }
        // 构建倒排索引
        bool BuildInvertedIndex(const DocInfo &doc)
        {
            // DocInfo[title, content, url, doc_id]
            // 单词计数
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;
                word_cnt() : title_cnt(0), content_cnt(0) {}
            };
            std::unordered_map<std::string, word_cnt> word_map; 
            
            // 对标题进行分词
            std::vector<std::string> title_words; // 雷军小米 -> 1.雷军 2.小米
            ns_util::JiebaUtil::SplitString(doc.title,&title_words);

            // 对标题进行词频统计
            for(std::string &s : title_words)
            {
                boost::to_lower(s); // 将我们的分词进行统一转化成为小写的
                word_map[s].title_cnt++; // 雷军title_cnt = 1 content_cnt = 0 小米title_cnt = 1 content_cnt = 0
            }

            // 对内容进行分词
            std::vector<std::string> content_words; // 雷军的小米公司 -> 1.雷军 2.小米 3.公司
            ns_util::JiebaUtil::SplitString(doc.content,&content_words);

            // 对内容进行词频统计
            for(auto &s : content_words)
            {
                boost::to_lower(s); // 将我们的分词进行统一转化成为小写的
                word_map[s].content_cnt++; // 雷军title_cnt = 1 content_cnt = 1 小米title_cnt = 1 content_cnt = 1 公司content_cnt = 1
            }
            
#define X 10
#define Y 1
            for(auto &word_pair : word_map)
            {
                InvertedElem item; // [词 , 文档id , 权重] 
                item.doc_id = doc.doc_id; 
                item.word = word_pair.first;
                item.weight = X * word_pair.second.title_cnt + Y * word_pair.second.content_cnt; // 相关性
                //std::unordered_map<std::string, InvertedElemList> inverted_index;
                //std::vector<InvertedElem> InvertedElemList; 
                //InvertedElem是每个词的属性
                InvertedElemList& inverted_list = inverted_index[word_pair.first];
                inverted_list.push_back(std::move(item));
            }
            return true;
        }
        // 单例模式
    };    
    Index* Index::instance = nullptr;
    std::mutex Index::lock;  
}